package com.yhy.common.log.util;

import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.yhy.common.log.enums.RequestTypeEnum;
import com.yhy.common.log.model.TrackLog;
import io.swagger.annotations.ApiOperation;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.http.MediaType;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

/**
 * @author Jax
 */
@Slf4j
@UtilityClass
public class AccessLogUtil {

    public TrackLog buildTrackLogBasicInfo(HttpServletRequest request, ProceedingJoinPoint point, RequestTypeEnum type) {
        TrackLog trackLog = new TrackLog();
        trackLog.setType(type.toString());
        trackLog.setTitle(getTitle(point));
        trackLog.setHandlerMethodName(getHandlerMethod(point));
        trackLog.setRequestMethod(request.getMethod());
        trackLog.setRequestUri(request.getRequestURI());
        trackLog.setRequestParam(getParamStr(request, point));
        trackLog.setRequestHeader(getHeaderMap(request));
        return trackLog;
    }

    public String getTrackLogString(TrackLog trackLog, RequestTypeEnum type) {
        StringJoiner logInfo = new StringJoiner("\n", "\n", "");
        String path = trackLog.getTitle() + "  " + trackLog.getRequestMethod() + ": " + trackLog.getRequestUri();
        logInfo.add("[" + type + "::TRACK:: REQUEST_TITLE]: " + path);
        logInfo.add("[" + type + "::TRACK::HANDLER_METHOD]: " + trackLog.getHandlerMethodName());
        logInfo.add("[" + type + "::TRACK::REQUEST_HEADER]: " + trackLog.getRequestHeader());
        logInfo.add("[" + type + "::TRACK:: REQUEST_PARAM]: " + trackLog.getRequestParam());
        if (trackLog.isSuccess()) {
            logInfo.add("[" + type + "::TRACK:: RESPONSE_DATA]: " + trackLog.getData());
        } else {
            logInfo.add("[" + type + "::TRACK::    ERROR_CODE]: " + trackLog.getCode());
            logInfo.add("[" + type + "::TRACK:: ERROR_MESSAGE]: " + trackLog.getMessage());
        }
        logInfo.add("[" + type + "::TRACK:: RESPONSE_BODY]: " + trackLog.getResponseBody());
        return logInfo.toString();
    }

    public String getHandlerMethod(ProceedingJoinPoint point) {
        return (point.toShortString())
                .replace("execution", "")
                .replace("(..)", "")
                .replace(")", "")
                .replace("(", "");
    }

    private String getParamStr(HttpServletRequest request, ProceedingJoinPoint point) {
        String paramString = "";
        if (isJson(request)) {
            try {
                paramString = JSONUtil.toJsonStr(point.getArgs());
            } catch (Throwable e) {
                // uncheck
            }
        } else {
            paramString = HttpUtil.toParams(request.getParameterMap());
        }
        return paramString;
    }

    private Map<String, String> getHeaderMap(HttpServletRequest request) {
        Map<String, String> headerMap = new HashMap<>();
        Enumeration<String> enumeration = request.getHeaderNames();
        while (enumeration.hasMoreElements()) {
            String name = enumeration.nextElement();
            String value = request.getHeader(name);
            headerMap.put(name, value);
        }
        return headerMap;
    }

    private String getTitle(JoinPoint joinPoint) {
        String value = "";
        try {
            Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
            ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
            if (null != apiOperation) {
                value = apiOperation.value();
            }
        } catch (Exception e) {
            // 解析ApiOperation接口描述异常 uncheck
        }
        return value;
    }

    private boolean isJson(HttpServletRequest request) {
        if (request.getContentType() != null) {
            return request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE);
        }
        return false;
    }

}
